package middleware

import (
	"context"
	"fmt"

	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

const (
	ClientHeaderKey = "client-id"
	ClientSecretKey = "client-secret"
)

var (
	errMissingMetadata = status.Errorf(codes.InvalidArgument, "missing metadata")
	errInvalidToken    = status.Errorf(codes.Unauthenticated, "invalid token")
)

func ClientCredentialsHeader(ak, sk string) metadata.MD {

	return metadata.MD{
		ClientHeaderKey: []string{ak},
		ClientSecretKey: []string{sk},
	}

}

// func NewAuthUnaryServerInterceptor() grpc.UnaryServerInterceptor {
// 	return (&GrpcAuther{}).UnaryServerInterceptor
// }

// func NewAuthStreamServerInterceptor() grpc.StreamServerInterceptor {
// 	return (&GrpcAuther{}).StreamServerInterceptor
// }

type GrpcAuther struct {
	ClientId  string
	ClientKey string
}

func NewGrpcAuther() *GrpcAuther {
	return &GrpcAuther{
		ClientId:  "",
		ClientKey: "",
	}
}

func (s *GrpcAuther) WithPerRPCCredentials(clientId, clientKey string) {

	s.ClientId = clientId
	s.ClientKey = clientKey
}

func (s *GrpcAuther) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {

	return map[string]string{
		ClientHeaderKey: s.ClientId,
		ClientSecretKey: s.ClientKey,
	}, nil
}

func (s *GrpcAuther) RequireTransportSecurity() bool {
	return false
}

func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {

	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return nil, errMissingMetadata
	}
	fmt.Println(md)

	if err := valid(ctx, md); err != nil {
		return nil, errInvalidToken

	}
	return handler(ctx, req)

}

func StreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {

	md, ok := metadata.FromIncomingContext(ss.Context())
	if !ok {
		return errMissingMetadata
	}
	fmt.Println(md)

	if err := valid(ss.Context(), md); err != nil {
		return errInvalidToken

	}
	return handler(srv, ss)
}

func valid(ctx context.Context, md metadata.MD) error {

	var ak, sk string
	akarr := md.Get(ClientHeaderKey)
	skarr := md.Get(ClientSecretKey)

	if len(akarr) > 0 && len(skarr) > 0 {
		ak = akarr[0]
		sk = skarr[0]

		if ak != "admin" || sk != "1234" {
			return status.Errorf(codes.Unauthenticated, "client-id or client-secret invalid")
		}
		return nil

	} else {
		return status.Errorf(codes.Unauthenticated, "invalid token")
	}

}
